package fatboy.communication;

import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.prefs.Preferences;

public class ObjectID implements Serializable, Comparable{
	public final IDNumber staticID = new IDNumber(0);
	private final class IDNumber implements Serializable, Comparable{
		/**
		 * 
		 */
		private static final long serialVersionUID = -1L;
		
		private long id;
		private IDNumber(long l){
			id = l;
			if(id < 0){
				throw new IllegalArgumentException();
			}
		}
		public String toString(){
			return Long.toString(id);
		}
		private Long getLong() {
			return id;
		}
		public boolean equals(Object o){
			if(!(o instanceof IDNumber)){
				return false;
			}
			return ((IDNumber)o).id == id;
		}
		public int hashCode(){
			return (int) id;
		}
		public int compareTo(Object idNumberIn) {
			// TODO Auto-generated method stub
			return (int) (id - ((IDNumber)idNumberIn).id);
		}
	}

	/**
	 * 
	 */
	private final int hashMod = 31;
	
	private static final long serialVersionUID = -2L;
	private String computer;
	private IDNumber idNumber;
	private PortNumber port;
	/**
	 * generates a new ObjectID and gives it the location of the current thread
	 */
	public ObjectID(){
		Preferences prefs = Preferences.userNodeForPackage(this.getClass());
		Long oldNum = Long.parseLong(prefs.get("numberOfObjects", "0"));
		idNumber = new IDNumber(oldNum+1);
		prefs.put("numberOfObjects",idNumber.toString());
		try {
			computer = java.net.InetAddress.getLocalHost().getHostName();
			//System.out.println(idNumber);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		port = PortNumber.getRuntimePort();
	}
	public ObjectID(boolean staticObj){
		if(staticObj){
			idNumber = staticID ;
		}
		else
		{
			Preferences prefs = Preferences.userNodeForPackage(this.getClass());
			Long oldNum = Long.parseLong(prefs.get("numberOfObjects", "0"));
			idNumber = new IDNumber(oldNum+1);
			prefs.put("numberOfObjects",idNumber.toString());
			
		}
		try {
			computer = java.net.InetAddress.getLocalHost().getHostName();
			//System.out.println(idNumber);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		port = PortNumber.getRuntimePort();
	}
	/**
	 *  generates an objectID with the given long for the id and this threads location
	 * @param id id number to be used
	 * @throws IllegalArgumentException if id is negative
	 */
	public ObjectID(long id){
		Preferences prefs = Preferences.userNodeForPackage(this.getClass());
//		long oldNum = Long.parseLong(prefs.get("numberOfObjects", "0"));
		idNumber = new IDNumber(id);
		prefs.put("numberOfObjects", idNumber.toString());
		try {
			computer = java.net.InetAddress.getLocalHost().getHostName();
			//System.out.println(idNumber);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		port = PortNumber.getRuntimePort();
	}
	/**
	 *  generates an objectID with the given port number but still a unique number
	 * @param portIn port to be used
	 * @throws IllegalArgumentException if id is not a valid ObjectID number
	 */
	public ObjectID(PortNumber portIn){
		Preferences prefs = Preferences.userNodeForPackage(this.getClass());
		long oldNum = Long.parseLong(prefs.get("numberOfObjects", "0"));
		idNumber = new IDNumber(oldNum);
		prefs.put("numberOfObjects", idNumber.toString());
		try {
			computer = java.net.InetAddress.getLocalHost().getHostName();
			//System.out.println(idNumber);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		port = portIn;
	}
	/**
	 * constructor mostly for debug and testing accepts all values
	 * @param comp computer name
	 * @param id ID number
	 * @param portIn port to be contacted on
	 */
	public ObjectID(String comp, long id, PortNumber portIn){
		computer = comp;
		idNumber = new IDNumber(id);
		port = new PortNumber(portIn.getPort());
	}
	/**
	 *  a copy constructor
	 * @param other
	 */
	public ObjectID(ObjectID other) {
		computer = other.computer;
		idNumber = other.idNumber;
		port = new PortNumber(other.port.getPort());
	}
	public boolean equals(Object o){
		//System.out.println("Equals");
		if(o instanceof ObjectID){
			ObjectID other = (ObjectID) o;
			return computer.equalsIgnoreCase(other.computer)&& idNumber.equals(other.idNumber);
		}
		return false;
	}
	/**
	 * returns the stored computer name
	 * @return string of the computer's name
	 */
	public String getComputer(){
		return computer;
	}
	/**
	 * returns the stored Port Number
	 * @return the PortNumber
	 */
	public PortNumber getPort(){
		return port;
	}
	/**
	 * returns the id number
	 * @return the id number associated with this Object
	 */
	public Long getObjID(){
		return idNumber.getLong();
	}
	
	public int hashCode(){
		return (int) (computer.toUpperCase().hashCode() + hashMod * idNumber.hashCode());
	}
	/**
	 * clears out the system wide ObjectID counter to 0
	 */
	public static void main(String[] args){
		clearNumber();
	}
	/**
	 * clears out the system wide ObjectID counter to 0
	 */
	public static void clearNumber(){
		Preferences prefs = Preferences.userNodeForPackage(ObjectID.class);
		prefs.put("numberOfObjects", Long.toString(1));
	}
	
	public String toString(){
		return idNumber + ", " + computer + ", " + port.toString();
	}
	public int compareTo(Object id) {
		return idNumber.compareTo(((ObjectID)id).idNumber);
	}
	public boolean isStatic(){
		return idNumber.equals(staticID);
	}
}
